package org.opendolphin.demo.lazy; import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.effect.DropShadowBuilder; import javafx.scene.layout.BorderPane; import javafx.scene.layout.ColumnConstraintsBuilder; import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPaneBuilder; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.util.Callback; import jfxtras.labs.scene.control.gauge.Gauge; import jfxtras.labs.scene.control.gauge.Radial; import jfxtras.labs.scene.control.gauge.StyleModel; import org.opendolphin.binding.JFXBinder; import org.opendolphin.core.ModelStoreEvent; import org.opendolphin.core.ModelStoreListener; import org.opendolphin.core.client.ClientDolphin; import org.opendolphin.core.client.ClientPresentationModel; import org.opendolphin.core.client.comm.OnFinishedHandlerAdapter; import org.opendolphin.core.client.comm.WithPresentationModelHandler; import org.opendolphin.demo.FX; import java.util.*; import static org.opendolphin.demo.DemoStyle.*; import static org.opendolphin.demo.lazy.LazyLoadingConstants.ATT.*; import static org.opendolphin.demo.lazy.LazyLoadingConstants.TYPE.*; /** * A demo that shows how to easily do lazy loading with the standard Dolphin on-board means. * Simply start and see how the table on the left-hand-side fills - depending on the sleepMillis * more or less quickly. * The details show how big the "imaginary" table is and how many items are loaded lazily from the server. * Clicking on any row will display the details in the textField - loaded as a presentation model. * Clicking in a not-yet-lazily loaded row does nothing. */ public class LazyLoadingView extends Application { private static ClientDolphin dolphin; public static void setClientDolphin(ClientDolphin clientDolphin) { dolphin = clientDolphin; } @Override public void start(Stage stage) throws Exception { List<String> attributeNames = Arrays.asList(ID, FIRST_LAST, LAST_FIRST, CITY, PHONE); final ClientPresentationModel dataMold = dolphin.presentationModel("dataMold", attributeNames); final ObservableList<Map> observableList = FXCollections.observableArrayList(); final Radial gauge = new Radial(new StyleModel()); gauge.getStyleModel().setFrameDesign(Gauge.FrameDesign.CHROME); gauge.setTitle("Real Load %"); gauge.setLcdDecimals(3); gauge.setPrefSize(250, 250); gauge.setEffect(DropShadowBuilder.create().radius(20).color(new Color(0,0,0,0.4)).build()); TableColumn<Map, String> nameCol = new TableColumn<Map, String>(); nameCol.setText("Name"); nameCol.setPrefWidth(150); nameCol.setSortable(false); // sorting needs all data to be loaded TableColumn<Map, String> cityCol = new TableColumn<Map, String>(); cityCol.setText("City"); cityCol.setPrefWidth(150); cityCol.setSortable(false); // sorting needs all data to be loaded TableView<Map> table = new TableView<Map>(); table.setPrefWidth(300); table.getColumns().add(nameCol); table.getColumns().add(cityCol); table.setItems(observableList); // cell values are lazily requested from JavaFX and must return an observable value nameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map, String>, ObservableValue<String>>() { @Override public ObservableValue<String> call(TableColumn.CellDataFeatures cellDataFeatures) { Map valueMap = (Map) cellDataFeatures.getValue(); String lazyId = valueMap.get("id").toString(); // it.value['id']; final SimpleStringProperty placeholder = new SimpleStringProperty("..."); dolphin.getClientModelStore().withPresentationModel(lazyId, new WithPresentationModelHandler() { public void onFinished(ClientPresentationModel presentationModel) { String value = presentationModel.getAt(LAST_FIRST).getValue().toString(); placeholder.setValue(value); // fill async lazily } }); return placeholder; } }); cityCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map, String>, ObservableValue<String>>() { @Override public ObservableValue<String> call(TableColumn.CellDataFeatures cellDataFeatures) { Map valueMap = (Map) cellDataFeatures.getValue(); String lazyId = valueMap.get("id").toString(); final SimpleStringProperty placeholder = new SimpleStringProperty("..."); dolphin.getClientModelStore().withPresentationModel(lazyId, new WithPresentationModelHandler() { public void onFinished(ClientPresentationModel presentationModel) { String value = presentationModel.getAt(CITY).getValue().toString(); placeholder.setValue(value); // fill async lazily } }); return placeholder; } }); // when a table row is selected, we fill the mold and the detail view gets updated table.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Map>() { @Override public void changed(ObservableValue<? extends Map> selected, Map o, Map oldVal) { Map selectedPm = selected.getValue(); String pmId = (selectedPm == null) ? null : selectedPm.get("id").toString(); dolphin.getClientModelStore().withPresentationModel(pmId, new WithPresentationModelHandler() { public void onFinished(ClientPresentationModel presentationModel) { dolphin.apply(presentationModel).to(dataMold); } }); } }); final TextField nameField = new TextField(); final TextField cityField = new TextField(); final TextField phoneField = new TextField(); final Label lazilyLoadedField = new Label(); final Label tableSizeField = new Label(); final Label selectedIndexField = new Label(); GridPane centerView = GridPaneBuilder.create().hgap(10).vgap(10).padding(new Insets(20, 20, 20, 20)).build(); centerView.getColumnConstraints().add(ColumnConstraintsBuilder.create().halignment(HPos.RIGHT).build()); centerView.add(new Label("Name"), 0, 0); centerView.add(nameField, 1, 0); centerView.add(new Label("City"), 0, 1); centerView.add(cityField, 1, 1); centerView.add(new Label("Phone"), 0, 2); centerView.add(phoneField, 1, 2); centerView.add(new Label("table size"), 0, 3); centerView.add(tableSizeField, 1, 3); centerView.add(new Label("lazily loaded"), 0, 4); centerView.add(lazilyLoadedField, 1, 4); centerView.add(new Label("selected index"), 0, 5); centerView.add(selectedIndexField, 1, 5); centerView.add(gauge, 1, 6); // all the bindings ... JFXBinder.bind(ID).of(dataMold).to(FX.TEXT).of(selectedIndexField); JFXBinder.bind(FIRST_LAST).of(dataMold).to(FX.TEXT).of(nameField); JFXBinder.bind(CITY).of(dataMold).to(FX.TEXT).of(cityField); JFXBinder.bind(PHONE).of(dataMold).to(FX.TEXT).of(phoneField); // count the number of lazily loaded pms by listing to the model store final Counter counter = new Counter(); dolphin.addModelStoreListener(LAZY, new ModelStoreListener() { public void modelStoreChanged(ModelStoreEvent evt) { if (evt.getType() == ModelStoreEvent.Type.ADDED) { counter.increment(); lazilyLoadedField.setText(String.valueOf(counter.getValue())); } } }); // count the number of lazily loaded pms by listing to the model store dolphin.addModelStoreListener(LAZY, new ModelStoreListener() { public void modelStoreChanged(ModelStoreEvent evt) { if (evt.getType() == ModelStoreEvent.Type.ADDED) { gauge.setValue(100d * counter.getValue() / observableList.size()); } } }); // when starting, first fill the table with pm ids dolphin.send("fullDataRequest", new OnFinishedHandlerAdapter() { @Override public void onFinishedData(List<Map> data) { for (Map map : data) { observableList.add(map); } tableSizeField.setText(String.valueOf(observableList.size())); } }); // TODO set margins of 10 BorderPane borderPane = new BorderPane(); borderPane.setLeft(table); borderPane.setCenter(centerView); Scene scene = new Scene(borderPane, 700, 500); defaultStyle(scene); stage.setScene(scene); stage.setTitle("Dolphin lazy loading demo"); stage.show(); } }